home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2010 April
/
PCWorld0410.iso
/
pluginy Firefox
/
138
/
138.xpi
/
chrome
/
stumbleupon.jar
/
content
/
DatabaseConnection.js
< prev
next >
Wrap
Text File
|
2009-05-22
|
26KB
|
1,122 lines
/*
[IP:]
***** BEGIN LICENSE BLOCK *****
Copyright (c) 2006 Center for History and New Media
George Mason University, Fairfax, Virginia, USA
http://chnm.gmu.edu
Licensed under the Educational Community License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.opensource.org/licenses/ecl1.php
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
***** END LICENSE BLOCK *****
StumbleUpon revisions include:
1. Changes namespace.
2. Changes prototype syntax.
3. Changes bracket convention.
4. Adds missing semicolons.
5. Replaces Zotero.getZoteroDatabase() with new method
this.getDBFile().
6. Replaces Zotero.getZoteroDirectory() with new method
this._getStorageDirectory().
7. Replaces Zotero.moveToUnique() with new method
this._moveToUniqueFile().
8. New debug logging that doesn't rely on the Zotero object.
9. New user interaction that doesn't rely on the Zotero object.
10. Adds this._query and the a, as, av, als, alv, q, avr and v
methods.
11. Makes query() always return an array for SELECT queries.
12. Added _parent and automatic backup disabling.
13. Added _modified and made automatic backup dependent on
_modified.
14. Disabled prompt for restart upon detecting corruption.
15. Added _hostIsWindows().
*/
var su_DatabaseConnection = function(parent, dbName)
{
this.skipBackup = false;
// Private members
this._parent = parent;
this._dbName = dbName;
this._modified = false;
this._shutdown = false;
this._connection = null;
this._transactionRollback = null;
this._transactionNestingLevel = 0;
this._callbacks = { begin: [], commit: [], rollback: [] };
this._dbIsCorrupt = null;
this._self = this;
this._query = "";
}
su_DatabaseConnection.prototype =
{ // BEGIN prototype
/////////////////////////////////////////////////////////////////
//
// Public methods
//
/////////////////////////////////////////////////////////////////
/*
* Run an SQL query
*
* Optional _params_ is an array of bind parameters in the form
* [1,"hello",3] or [{'int':2},{'string':'foobar'}]
*
* Returns:
* - Associative array (similar to mysql_fetch_assoc) for SELECT's
* - lastInsertId for INSERT's
* - TRUE for other successful queries
* - FALSE on error
*/
query: function (sql,params)
{
var db = this._getDBConnection();
if (! sql)
sql = this._query;
try {
// Parse out the SQL command being used
var op = sql.match(/^[^a-z]*[^ ]+/i);
if (op)
op = op.toString().toLowerCase();
// If SELECT statement, return result
if (op=='select')
{
// Until the native dataset methods work (or at least exist),
// we build a multi-dimensional associative array manually
var statement = this.getStatement(sql, params);
var dataset = new Array();
while (statement.executeStep())
{
var row = new Array();
for(var i=0, len=statement.columnCount; i<len; i++)
row[statement.getColumnName(i)] = this._getTypedValue(statement, i);
dataset.push(row);
}
statement.reset();
return dataset;
}
else
{
this._modified = true;
if (params)
{
var statement = this.getStatement(sql, params);
statement.execute();
}
else
{
this._debug(sql,5);
db.executeSimpleSQL(sql);
}
if (op=='insert')
return db.lastInsertRowID;
// DEBUG: Can't get affected rows for UPDATE or DELETE?
else
return true;
}
}
catch (e) {
this.checkException(e);
var dberr = (db.lastErrorString!='not an error')
? ' [ERROR: ' + db.lastErrorString + ']' : '';
throw(e + ' [QUERY: ' + sql + ']' + dberr);
}
},
/*
* Query a single value and return it
*/
valueQuery: function (sql,params)
{
var statement = this.getStatement(sql, params);
// No rows
if (!statement.executeStep())
{
statement.reset();
return false;
}
var value = this._getTypedValue(statement, 0);
statement.reset();
return value;
},
/*
* Run a query and return the first row
*/
rowQuery: function (sql,params)
{
var result = this.query(sql,params);
if (result.length)
return result[0];
else
return new Array();
},
/*
* Run a query and return the first column as a numerically-indexed array
*/
columnQuery: function (sql,params)
{
var statement = this.getStatement(sql, params);
if (statement)
{
var column = new Array();
while (statement.executeStep())
column.push(this._getTypedValue(statement, 0));
statement.reset();
return column.length ? column : false;
}
return false;
},
a: function (str)
{
this._query = str;
},
av: function (str)
{
str = ((parseInt(str)) + "");
if (str == "NaN")
str = "0";
this._query += str + ",";
},
avr: function (str)
{
str = ((parseInt(str)) + "");
if (str == "NaN")
str = "0";
this._query += str;
},
as: function (str)
{
this._query += "'" + str.replace(/'/g, "''") + "',";
},
alv: function (str)
{
str = ((parseInt(str)) + "");
if (str == "NaN")
str = "0";
this._query += str + ")";
},
als: function (str)
{
this._query += "'" + str.replace(/'/g, "''") + "')";
},
escapeString: function (str)
{
this._query += str.replace(/'/g, "''");
},
q: function (str)
{
return "'" + str.replace(/'/g, "''") + "'";
},
v: function (str)
{
str = ((parseInt(str)) + "");
if (str == "NaN")
str = "0";
return str;
},
/*
/*
* Get a raw mozStorage statement from the DB for manual processing
*
* This should only be used externally for manual parameter binding for
* large repeated queries
*
* Optional _params_ is an array of bind parameters in the form
* [1,"hello",3] or [{'int':2},{'string':'foobar'}]
*/
getStatement: function (sql, params)
{
var db = this._getDBConnection();
try {
this._debug(sql,5);
var statement = db.createStatement(sql);
}
catch (e) {
var dberr = (db.lastErrorString!='not an error')
? ' [ERROR: ' + db.lastErrorString + ']' : '';
throw(e + ' [QUERY: ' + sql + ']' + dberr);
}
if (params)
{
// If single scalar value or single non-array object, wrap in an array
if (typeof params != 'object' || params===null ||
(params && typeof params == 'object' && !params.length))
params = [params];
for (var i=0; i<params.length; i++)
{
// Integer
if (params[i]!==null && typeof params[i]['int'] != 'undefined')
{
var type = 'int';
var value = params[i]['int'];
}
// String
else if (params[i]!==null && typeof params[i]['string'] != 'undefined')
{
var type = 'string';
var value = params[i]['string'];
}
// Null
else if (params[i]!==null && typeof params[i]['null'] != 'undefined')
{
var type = 'null';
}
// Automatic (trust the JS type)
else
{
switch (typeof params[i])
{
case 'string':
var type = 'string';
break;
case 'number':
var type = 'int';
break;
// Object
default:
if (params[i]===null)
{
var type = 'null';
}
else
{
throw('Invalid bound parameter ' + params[i]);
// throw('Invalid bound parameter ' + params[i] +
// ' in ' + Zotero.varDump(params));
}
}
var value = params[i];
}
// Bind the parameter as the correct type
switch (type)
{
case 'int':
this._debug('Binding parameter ' + (i+1)
+ ' of type int: ' + value, 5);
statement.bindInt32Parameter(i, value);
break;
case 'string':
this._debug('Binding parameter ' + (i+1)
+ ' of type string: "' + value + '"', 5);
statement.bindUTF8StringParameter(i, value);
break;
case 'null':
this._debug('Binding parameter ' + (i+1)
+ ' of type NULL', 5);
statement.bindNullParameter(i);
break;
}
}
}
return statement;
},
/*
* Only for use externally with this.getStatement()
*/
getLastInsertID: function ()
{
var db = this._getDBConnection();
return db.lastInsertRowID;
},
/*
* Only for use externally with this.getStatement()
*/
getLastErrorString: function ()
{
var db = this._getDBConnection();
return db.lastErrorString;
},
beginTransaction: function ()
{
var db = this._getDBConnection();
if (db.transactionInProgress)
{
this._transactionNestingLevel++;
this._debug('Transaction in progress -- increasing level to '
+ this._transactionNestingLevel, 5);
}
else
{
this._debug('Beginning DB transaction', 5);
db.beginTransaction();
// Run callbacks
for (var i=0; i<this._callbacks.begin.length; i++)
{
if (this._callbacks.begin[i])
this._callbacks.begin[i]();
}
}
},
commitTransaction: function ()
{
var db = this._getDBConnection();
if (this._transactionNestingLevel)
{
this._transactionNestingLevel--;
this._debug('Decreasing transaction level to ' + this._transactionNestingLevel, 5);
}
else if (this._transactionRollback)
{
this._debug('Rolling back previously flagged transaction', 5);
this.rollbackTransaction();
}
else
{
this._debug('Committing transaction',5);
try {
db.commitTransaction();
// Run callbacks
for (var i=0; i<this._callbacks.commit.length; i++)
{
if (this._callbacks.commit[i])
this._callbacks.commit[i]();
}
}
catch(e) {
var dberr = (db.lastErrorString!='not an error')
? ' [ERROR: ' + db.lastErrorString + ']' : '';
throw(e + dberr);
}
}
},
rollbackTransaction: function ()
{
var db = this._getDBConnection();
if (!db.transactionInProgress)
{
this._debug("Transaction is not in progress in rollbackTransaction()", 2);
return;
}
if (this._transactionNestingLevel)
{
this._transactionNestingLevel--;
this._transactionRollback = true;
this._debug('Flagging nested transaction for rollback', 5);
}
else
{
this._debug('Rolling back transaction', 5);
this._transactionRollback = false;
try {
db.rollbackTransaction();
// Run callbacks
for (var i=0; i<this._callbacks.rollback.length; i++)
{
if (this._callbacks.rollback[i])
this._callbacks.rollback[i]();
}
}
catch(e) {
var dberr = (db.lastErrorString!='not an error')
? ' [ERROR: ' + db.lastErrorString + ']' : '';
throw(e + dberr);
}
}
},
addCallback: function (type, cb)
{
switch (type)
{
case 'begin':
case 'commit':
case 'rollback':
break;
default:
throw ("Invalid callback type '" + type + "' in DB.addCallback()");
}
var id = this._callbacks[type].length;
this._callbacks[type][id] = cb;
return id;
},
removeCallback: function (type, id)
{
switch (type)
{
case 'begin':
case 'commit':
case 'rollback':
break;
default:
throw ("Invalid callback type '" + type + "' in DB.removeCallback()");
}
delete this._callbacks[type][id];
},
transactionInProgress: function ()
{
var db = this._getDBConnection();
return db.transactionInProgress;
},
/**
* Safety function used on shutdown to make sure we're not stuck in the
* middle of a transaction
*
* NOTE: No longer used
*/
commitAllTransactions: function ()
{
if (this.transactionInProgress())
{
var level = this._transactionNestingLevel;
this._transactionNestingLevel = 0;
try {
this.commitTransaction();
}
catch (e) {}
return level ? level : true;
}
return false;
},
/*
* Used on shutdown to rollback all open transactions
*/
rollbackAllTransactions: function ()
{
if (this.transactionInProgress())
{
var level = this._transactionNestingLevel;
this._transactionNestingLevel = 0;
try {
this.rollbackTransaction();
}
catch (e) {}
return level ? level : true;
}
return false;
},
tableExists: function (table)
{
return this._getDBConnection().tableExists(table);
},
getColumns: function (table)
{
var db = this._getDBConnection();
try {
var sql = "SELECT * FROM " + table + " LIMIT 1";
var statement = this.getStatement(sql);
var cols = new Array();
for (var i=0,len=statement.columnCount; i<len; i++)
cols.push(statement.getColumnName(i));
statement.reset();
return cols;
}
catch (e) {
this._debug(e,1);
return false;
}
},
getColumnHash: function (table)
{
var cols = this.getColumns(table);
var hash = {};
if (cols.length)
{
for (var i=0; i<cols.length; i++)
hash[cols[i]] = true;
}
return hash;
},
/**
* Find the lowest unused integer >0 in a table column
*
* Note: This retrieves all the rows of the column, so it's not really
* meant for particularly large tables.
**/
getNextID: function (table, column)
{
var sql = 'SELECT ' + column + ' FROM ' + table + ' ORDER BY ' + column;
var vals = this.columnQuery(sql);
if (!vals)
return 1;
if (vals[0] === '0')
vals.shift();
for (var i=0, len=vals.length; i<len; i++)
{
if (vals[i] != i+1)
break;
}
return i+1;
},
/**
* Find the next lowest numeric suffix for a value in table column
*
* For example, if "Untitled" and "Untitled 2" and "Untitled 4",
* returns "Untitled 3"
*
* DEBUG: doesn't work once there's an "Untitled 10"
*
* If _name_ alone is available, returns that
**/
getNextName: function (table, field, name)
{
var sql = "SELECT " + field + " FROM " + table + " WHERE " + field
+ " LIKE ? ORDER BY " + field + " COLLATE NOCASE";
var untitleds = this.columnQuery(sql, name + '%');
if (!untitleds || untitleds[0]!=name)
return name;
var i = 1;
var num = 2;
while (untitleds[i] && untitleds[i]==(name + ' ' + num))
{
while (untitleds[i+1] && untitleds[i]==untitleds[i+1])
{
this._debug('Next ' + i + ' is ' + untitleds[i]);
i++;
}
i++;
num++;
}
return name + ' ' + num;
},
/*
* Shutdown observer -- implements nsIObserver
*/
observe: function(subject, topic, data)
{
switch (topic)
{
case 'xpcom-shutdown':
this.destroy();
break;
}
},
destroy: function ()
{
if (this._shutdown)
{
this._debug('returning');
return;
}
// NOTE: disabled
//var level = this.commitAllTransactions();
var level = this.rollbackAllTransactions();
if (level)
{
level = level === true ? '0' : level;
this._debug("A transaction in DB '" + this._dbName + "' was still open! (level " + level + ")", 2);
}
this._shutdown = true;
this.skipBackup = (this.skipBackup || (! this._modified) || (! this._parent.getValue("@enable_db_backup")));
this.backupDatabase();
this._parent = null;
},
integrityCheck: function ()
{
var ok = this.valueQuery("PRAGMA integrity_check");
return ok == 'ok';
},
checkException: function (e)
{
if (e.name && e.name == 'NS_ERROR_FILE_CORRUPTED')
{
// Write corrupt marker to data directory
var file = this.getDBFile(this._dbName, 'is.corrupt');
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.write('', 0);
foStream.close();
this._dbIsCorrupt = true;
// var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
// .getService(Components.interfaces.nsIPromptService);
//
// var buttonFlags = (ps.BUTTON_POS_0) * (ps.BUTTON_TITLE_IS_STRING)
// + (ps.BUTTON_POS_1) * (ps.BUTTON_TITLE_IS_STRING);
//
// var index = ps.confirmEx(null,
// Zotero.getString('general.error'),
// Zotero.getString('db.dbCorrupted', this._dbName) + '\n\n' + Zotero.getString('db.dbCorrupted.restart'),
// buttonFlags,
// Zotero.getString('general.restartNow'),
// Zotero.getString('general.restartLater'),
// null, null, {});
//
// if (index == 0)
// {
// var appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]
// .getService(Components.interfaces.nsIAppStartup);
// appStartup.quit(Components.interfaces.nsIAppStartup.eRestart);
// appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit);
// }
//
// Zotero.skipLoading = true;
// return false;
}
return true;
},
backupDatabase: function (suffix)
{
if (this.transactionInProgress())
{
this._debug("Transaction in progress--skipping backup of DB '" + this._dbName + "'", 2);
return false;
}
var corruptMarker = this.getDBFile(this._dbName, 'is.corrupt').exists();
if (this.skipBackup)
{
this._debug("Skipping backup of database '" + this._dbName + "'", 1);
return false;
}
else if (this._dbIsCorrupt || corruptMarker)
{
this._debug("Database '" + this._dbName + "' is marked as corrupt--skipping backup", 1);
return false;
}
this._debug("Backing up database '" + this._dbName + "'");
var file = this.getDBFile(this._dbName);
var backupFile = this.getDBFile(this._dbName,
(suffix ? suffix + '.' : '') + 'bak');
// Copy via a temporary file so we don't run into disk space issues
// after deleting the old backup file
var tmpFile = this.getDBFile(this._dbName, 'tmp');
if (tmpFile.exists())
tmpFile.remove(null);
try {
file.copyTo(file.parent, tmpFile.leafName);
}
catch (e){
// TODO: deal with low disk space
throw (e);
}
// Opened database files can't be moved on Windows, so we have to skip
// the extra integrity check (unless we wanted to write two copies of
// the database, but that doesn't seem like a great idea)
if (!this._hostIsWindows())
{
try {
var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService);
var connection = store.openDatabase(tmpFile);
}
catch (e){
this._debug("Database file '" + tmpFile.leafName + "' is corrupt--skipping backup");
if (tmpFile.exists())
tmpFile.remove(null);
return false;
}
}
// Remove old backup file
if (backupFile.exists())
backupFile.remove(null);
tmpFile.moveTo(tmpFile.parent, backupFile.leafName);
return true;
},
_hostIsWindows: function ()
{
var outval = true;
try {
var appinfo = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULAppInfo);
appinfo = appinfo.QueryInterface(Components.interfaces.nsIXULRuntime);
// spec.id = appinfo.ID;
// spec.version = appinfo.version;
outval = (appinfo.OS.toLowerCase().indexOf("win") != -1);
} catch (e) {}
return outval;
},
/*
* Keep the SQLite shared cache live between transactions with a dummy statement,
* which speeds up DB access dramatically (at least on Windows and Linux--OS X
* seems to be much faster already, perhaps due to its own disk cache)
*
* This is the same technique used by Mozilla code. The one downside is that it
* prevents schema changes, so this is called after schema updating. If the
* schema really needs to be updated at another point, use stopDummyStatement().
*
* See http://developer.mozilla.org/en/docs/Storage:Performance for more info.
*/
startDummyStatement: function ()
{
// try {
if (!this._dummyConnection)
{
this._debug("Opening database '" + this._dbName + " for dummy statement");
// Get the storage service
var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService);
var file = this.getDBFile(this._dbName);
this._dummyConnection = store.openDatabase(file);
}
if (this._dummyStatement)
{
this._debug("Dummy statement is already open");
return;
}
this._debug("Initializing dummy statement for '" + this._dbName + "'");
var sql = "CREATE TABLE IF NOT EXISTS dummyTable (id INTEGER PRIMARY KEY)";
this._dummyConnection.executeSimpleSQL(sql);
sql = "INSERT OR IGNORE INTO dummyTable VALUES (1)";
this._dummyConnection.executeSimpleSQL(sql);
sql = "SELECT id FROM dummyTable LIMIT 1";
this._dummyStatement = this._dummyConnection.createStatement(sql);
this._dummyStatement.executeStep();
// }
// catch (e) {
// Components.utils.reportError(e);
// this._debug(e);
// }
},
/*
* Stop the dummy statement temporarily to allow for schema changess
*
* The statement needs to be started again or performance will suffer.
*/
stopDummyStatement: function ()
{
if (!this._dummyStatement)
return;
this._debug("Stopping dummy statement for '" + this._dbName + "'");
this._dummyStatement.reset();
this._dummyStatement = null;
},
getDBFile: function (name, ext)
{
var str;
if (name)
str = name;
else if (this._dbName)
str = this._dbName;
else
str = 'stumbleupon';
str += '.sqlite';
str += (ext) ? ('.' + ext) : '';
var file = this._getStorageDirectory();
file.append(str);
return file;
},
/////////////////////////////////////////////////////////////////
//
// Private methods
//
/////////////////////////////////////////////////////////////////
_getStorageDirectory: function ()
{
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsIFile);
file.append("StumbleUpon");
if (! file.exists())
file.create(file.DIRECTORY_TYPE, 0700);
return file;
},
_moveToUniqueFile: function (file, newFile)
{
newFile.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0644);
var newName = newFile.leafName;
newFile.remove(null);
// Move file to unique name
file.moveTo(newFile.parent, newName);
return file;
},
/*
* Retrieve a link to the data store
*/
_getDBConnection: function ()
{
if (this._connection)
return this._connection;
this._debug("Opening database '" + this._dbName + "'");
// Get the storage service
var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService);
var file = this.getDBFile(this._dbName);
var backupFile = this.getDBFile(this._dbName, 'bak');
var fileName = this._dbName + '.sqlite';
// if (this._dbName == 'zotero' && ZOTERO_CONFIG['DB_REBUILD'])
// {
// if (confirm('Erase all user data and recreate database from schema?'))
// {
// // Delete existing Zotero database
// if (file.exists())
// file.remove(null);
//
// // Delete existing storage folder
// var dir = Zotero.getStorageDirectory();
// if (dir.exists())
// dir.remove(true);
// }
// }
catchBlock: try {
var corruptMarker = this.getDBFile(this._dbName, 'is.corrupt');
if (corruptMarker.exists())
throw({ name: 'NS_ERROR_FILE_CORRUPTED' })
this._connection = store.openDatabase(file);
}
catch (e) {
if (e.name=='NS_ERROR_FILE_CORRUPTED')
{
this._debug("Database file '" + file.leafName + "' corrupted", 1);
// No backup file! Eek!
if (!backupFile.exists())
{
this._debug("No backup file for DB '" + this._dbName + "' exists", 1);
// Save damaged filed
this._debug('Saving damaged DB file with .damaged extension', 1);
var damagedFile = this.getDBFile(this._dbName, 'damaged');
this._moveToUniqueFile(file, damagedFile);
// Create new main database
var file = this.getDBFile(this._dbName);
this._connection = store.openDatabase(file);
if (corruptMarker.exists())
corruptMarker.remove(null);
// alert(Zotero.getString('db.dbCorruptedNoBackup', fileName));
break catchBlock;
}
// Save damaged file
this._debug('Saving damaged DB file with .damaged extension', 1);
var damagedFile = this.getDBFile(this._dbName, 'damaged');
this._moveToUniqueFile(file, damagedFile);
// Test the backup file
try {
this._connection = store.openDatabase(backupFile);
}
// Can't open backup either
catch (e) {
// Create new main database
var file = this.getDBFile(this._dbName);
this._connection = store.openDatabase(file);
// alert(Zotero.getString('db.dbRestoreFailed', fileName));
if (corruptMarker.exists())
corruptMarker.remove(null);
break catchBlock;
}
this._connection = undefined;
// Copy backup file to main DB file
this._debug("Restoring database '" + this._dbName + "' from backup file", 1);
try {
backupFile.copyTo(backupFile.parent, fileName);
}
catch (e) {
// TODO: deal with low disk space
throw (e);
}
// Open restored database
var file = this._getStorageDirectory();
file.append(fileName);
this._connection = store.openDatabase(file);
this._debug('Database restored', 1);
// var msg = Zotero.getString('db.dbRestored', [
// fileName,
// Zotero.Date.getFileDateString(backupFile),
// Zotero.Date.getFileTimeString(backupFile)
// ]);
// alert(msg);
if (corruptMarker.exists())
corruptMarker.remove(null);
break catchBlock;
}
// Some other error that we don't yet know how to deal with
throw (e);
}
// Register shutdown handler to call this.onShutdown() for DB backup
var observerService = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
observerService.addObserver(this, "xpcom-shutdown", false);
observerService = null;
return this._connection;
},
_debug: function (str, level)
{
//this._parent._logError(false, {}, "DB ", str);
},
_getTypedValue: function (statement, i)
{
var type = statement.getTypeOfIndex(i);
switch (type)
{
case statement.VALUE_TYPE_INTEGER:
return statement.getInt64(i);
case statement.VALUE_TYPE_TEXT:
return statement.getUTF8String(i);
case statement.VALUE_TYPE_NULL:
return null;
case statement.VALUE_TYPE_FLOAT:
return statement.getDouble(i);
case statement.VALUE_TYPE_BLOB:
return statement.getBlob(i);
default:
return null;
}
}
} // END prototype